﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Effects.Effects;
using Effects.Effects.GuiParts;


namespace EffectsPack
{
    /// <summary>
    ///  Implementation of Wah-wah effect.
    /// </summary>
    /// <remarks>
    ///   <para>
    ///     Wah-wah effect is produce by band-pass (or low-pass) filter which center frequency is controlled by user. User controls the frequency by
    ///     <see cref="EffectsPack.Pad"/> control.
    ///   </para>
    /// </remarks>
    public class Wah : Effect
    {
        #region controls

        #region frequency_control
        // controling center frequency of wah

        /// <summary>
        ///  Current center frequency.
        /// </summary>
        private double frequencyControl;
        
        /// <summary>
        ///  Controls center frequency of wah filter.
        /// </summary>
        private Pad FrequencyControlPad;

        #endregion

        #region master

        /// <summary>
        ///  Master volume level.
        /// </summary>
        private double master;

        /// <summary>
        ///  Controls master volume level.
        /// </summary>
        private Pot MasterPot;

        #endregion

        #region footswitch

        /// <summary>
        ///  Footswitch for by passing effect.
        /// </summary>
        private Footswitch footswitch;

        /// <summary>
        ///  Led indicating On/Off state.
        /// </summary>
        private LED led;

        /// <summary>
        ///  Synchornizer for footswitch and led.
        /// </summary>
        private FootswitchLedSynchronizer synchronizer;

        #endregion

        #endregion

        #region effect_properties

        /// <summary>
        ///  Gets the name of the effect.
        /// </summary>
        public override string Name
        {
            get
            {
                return "Wah-Wah 0.8";
            }
        }

        /// <summary>
        ///  Gets or sets the sampling rate of input signal.
        /// </summary>
        public override uint SampleRate
        {
            get{ return base.SampleRate; }
            set
            {
                sampleRate = value;
                InitializeFilter();
            }
        }

        #endregion

        #region constructor
        // constructor and initialization stuff

        /// <summary>
        ///  Creates new instance.
        /// </summary>
        public Wah()
        {
            BackgroundImage = System.Drawing.Image.FromStream(new System.IO.MemoryStream(EffectsPack.Properties.Resources.Wah));
            Size = BackgroundImage.Size;

            locker = new object();
            this.sign  = -1;

            InitializeComponents();
            InitializeFilter();
            InitializeFootswitch();
        }

        /// <summary>
        ///  Initializes Pad for frequency control.
        /// </summary>
        private void InitializeComponents()
        {
            FrequencyControlPad = new Pad();
            FrequencyControlPad.Size = new System.Drawing.Size(200, 160);
            FrequencyControlPad.Location = new System.Drawing.Point(90, 30);
            FrequencyControlPad.HorizontalValueChanged += new PotValueChangedEventHandler(this.FreqeuncyControlMoved);
            //FrequencyControlPad.BackColor = System.Drawing.Color.Red;
            FrequencyControlPad.ForegroundBrush = new System.Drawing.SolidBrush(System.Drawing.Color.LightBlue);
            FrequencyControlPad.Cursor = new System.Windows.Forms.Cursor(new System.IO.MemoryStream(EffectsPack.Properties.Resources.WahCursor));
            Controls.Add(FrequencyControlPad);

            MasterPot = new Pot();
            MasterPot.MaxValue = 5;
            MasterPot.MinValue = 0;
            MasterPot.Knob = PotKnob.ChickenKnobMiddle;
            MasterPot.Location = new System.Drawing.Point(5, 40);
            MasterPot.ValueChanged += new PotValueChangedEventHandler(this.MasterMoved);
            Controls.Add(MasterPot);
            MasterPot.Reset();

        }

        /// <summary>
        ///  Initializes wah filter.
        /// </summary>
        private void InitializeFilter()
        {
            filter = new TunableBandPass();
            
            normalizedLowerFreq = Math.PI * 200 / (sampleRate / 2F);
            normalizedFreqRange = Math.PI * 4000 / (sampleRate / 2F);

            double Q = Math.PI * 200 / (sampleRate / 2F);
            normalizedLowerQ = (2F - Math.Sqrt(4F - 4F * Math.Cos(Q) * Math.Cos(Q))) / (2F * Math.Cos(Q));

            Q = Math.PI * 3000 / (sampleRate / 2F);
            double upperQ = (2F - Math.Sqrt(4F - 4F * Math.Cos(Q) * Math.Cos(Q))) / (2F * Math.Cos(Q));
            normalizedQRange = upperQ - normalizedLowerQ;

            filter.Alfa = normalizedLowerQ;
            filter.Beta = Math.Cos(normalizedLowerFreq);
        }

        /// <summary>
        ///  Iniializes footswitch and led.
        /// </summary>
        private void InitializeFootswitch()
        {
            // footswitch
            footswitch = new Footswitch();
            switchedOn = true;
            footswitch.On = true;
            footswitch.Switched += new FootswitchSwitchedEventHandler(this.FootswitchSwitched);
            footswitch.Location = new System.Drawing.Point(20, 130);
            Controls.Add(footswitch);

            // led
            led = new LED();
            led.On = true;
            led.Location = new System.Drawing.Point(70, 130);
            Controls.Add(led);

            // synchronization
            synchronizer = new FootswitchLedSynchronizer(footswitch, led);
        }

        #endregion

        #region main_work

        /// <summary>
        ///  Wah filter.
        /// </summary>
        private TunableBandPass filter;

        /// <summary>
        ///  Normalized lower border for center frequency.
        /// </summary>
        private double normalizedLowerFreq;

        /// <summary>
        ///  Normalized range of center frequency.
        /// </summary>
        private double normalizedFreqRange;

        /// <summary>
        ///  Normalized lower border of filter's bandwidth.
        /// </summary>
        private double normalizedLowerQ;

        /// <summary>
        ///  Normalized range of filter's bandwidth.
        /// </summary>
        private double normalizedQRange;

        /// <summary>
        ///  Multithread lock.
        /// </summary>
        private object locker;

        /// <summary>
        ///  Signum used in effects method - possible future modification: switching between band-pass and band-stop filter.
        /// </summary>
        private double sign;

        /// <summary>
        ///  Effects input signal.
        /// </summary>
        /// <param name="data"> Array containing part of the input signal. </param>
        /// <returns> Effected output signal. </returns>
        public override double[] effect(double[] data)
        {
            if (switchedOn)
            {

                double[] outData = new double[data.Length];
                for (int i = 0; i < data.Length; i++)
                {
                    lock (locker)
                    {
                        outData[i] = master * (data[i] + sign * filter.Step(data[i]));
                    }
                }
                return outData;
            }

            return data;
        }

        #endregion

        #region controls_event_handlers
        // controling event caused by user.

        /// <summary>
        ///  Updates filter according to frequencyControl field.
        /// </summary>
        private void UpdateFilter()
        {
            lock (locker)
            {
                filter.Beta = Math.Cos(normalizedLowerFreq + frequencyControl * normalizedFreqRange);
                filter.Alfa = normalizedLowerQ + frequencyControl * normalizedQRange;
            }
        }

        /// <summary>
        ///  FrequencyControlPad event handler.
        /// </summary>
        /// <param name="sender"> Object that has raised event. </param>
        /// <param name="e"> Event arguments. </param>
        private void FreqeuncyControlMoved(object sender, PotValueChangedEventArgs e)
        {
            frequencyControl = e.NewValue;
            UpdateFilter();
        }

        /// <summary>
        ///  Master pot event handler.
        /// </summary>
        /// <param name="sender"> Object that has raised event. </param>
        /// <param name="e"> Event arguments. </param>
        private void MasterMoved(object sender, PotValueChangedEventArgs e)
        {
            master = e.NewValue;
        }

        /// <summary>
        ///  Footswitch.Switched event handler. According to the event arguments signal bypasses or runs through the effect.
        /// </summary>
        /// <param name="sender"> Object that has raised the event. </param>
        /// <param name="args"> Event Arguments </param>
        private void FootswitchSwitched(object sender, FootswitchSwitchedEventArgs args)
        {
            this.switchedOn = args.On;
        }

        #endregion

        #region settings
        // dummy implementations, this effect doesn't support recording

        /// <summary>
        ///  Dummy,this effect doesn't support recording
        /// </summary>
        /// <returns> Empty array </returns>
        public override EffectParam[] GetParameters()
        {
            return new EffectParam[0];
        }

        /// <summary>
        ///  Dummy,this effect doesn't support recording
        /// </summary>
        /// <returns> Empty array </returns>
        public override object[] GetAllSettings()
        {
            return new object[0];
        }

        #endregion

    }
}

